//! 包装OS的错误码, 统一错误码的查询和获取接口
//! 1. fn errno() -> i32;
//! 2. fn set_errno(i32);
//! 3. unsafe fn errmsg(i32) -> &str;
//!
//! 封装i32为Error
//!
//! # Example
//! ```rust
//! use hierr::*;
//!
//! set_errno(100);
//! let err = Error::last_error();
//! assert_eq!(err, 100.into());
//! ```

#![no_std]

use core::fmt;
use core::slice;
use core::str;

#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Error {
    pub errno: i32,
}

impl Error {
    pub const fn new(errno: i32) -> Self {
        Self { errno }
    }
    pub fn last_error() -> Self {
        Self { errno: errno() }
    }
}

impl Default for Error {
    fn default() -> Self {
        Error::new(-1)
    }
}

impl From<i32> for Error {
    fn from(i32: i32) -> Self {
        Self::new(i32)
    }
}

impl From<Error> for Result<(), Error> {
    fn from(err: Error) -> Self {
        if err.errno == 0 {
            Ok(())
        } else {
            Err(err)
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(f, "{}, {}", self.errno, unsafe { errmsg(self.errno) })
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(f, "{}, {}", self.errno, unsafe { errmsg(self.errno) })
    }
}

cfg_if::cfg_if! {
    if #[cfg(unix)] {
        #[link(name = "c")]
        extern "C" {
            #[cfg(any(
                target_os = "linux",
                target_os = "redox",
                target_os = "dragonfly",
                target_os = "fuchsia"
            ))]
            #[link_name = "__errno_location"]
            fn errno_location() -> *mut i32;

            #[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))]
            #[link_name = "__errno"]
            fn errno_location() -> *mut i32;

            #[cfg(any(target_os = "ios", target_os = "macos", target_os = "freebsd"))]
            #[link_name = "__error"]
            fn errno_location() -> *mut i32;

            #[cfg(any(target_os = "illumos", target_os = "solaris"))]
            #[link_name = "___errno"]
            fn errno_location() -> *mut i32;

            #[cfg(any(target_os = "haiku"))]
            #[link_name = "_errnop"]
            fn errno_location() -> *mut i32;

            fn strerror(errno: i32) -> *const u8;
            fn strlen(s: *const u8) -> usize;
        }

        pub fn errno() -> i32 {
            // # Safety
            // errno_location总是返回有效地址
            unsafe { *errno_location() }
        }

        pub fn set_errno(errno: i32) {
            // # Safety
            // errno_location总是返回有效地址
            unsafe { *errno_location() = errno };
        }

        /// # Safety
        /// strerror不支持重入, 其内容可能被后续调用修改
        pub unsafe fn errmsg<'a>(errno: i32) -> &'a str {
            let s = strerror(errno);
            let len = strlen(s);
            str::from_utf8_unchecked(slice::from_raw_parts(s, len))
        }

    } else if #[cfg(windows)] {
        pub fn errmsg<'a>(errno: i32) -> &'a str {
            "unknown"
        }

        #[link(name = "Kernel32")]
        extern "C" {
            #[link_name = "GetLastError"]
            pub fn errno() -> i32;
            #[link_name = "SetLastError"]
            pub fn set_errno(errno: i32);
        }
    }
}

pub mod prelude {
    pub const EPERM: i32 = 1;
    pub const ENOENT: i32 = 2;
    pub const EAGAIN: i32 = 11;
    pub const ENOMEM: i32 = 12;
    pub const EEXIST: i32 = 17;
    pub const EINVAL: i32 = 22;
    pub const ERANGE: i32 = 34;
    pub const ETIMEDOUT: i32 = 110;
    pub const ECANCELED: i32 = 125;
}
